home *** CD-ROM | disk | FTP | other *** search
/ Risc World 5 / Risc World 5.iso / SOFTWARE / Issue5 / PD / DIRSYNC / LegalStuff / gnudiff / dir.c < prev    next >
C/C++ Source or Header  |  2004-12-19  |  9KB  |  345 lines

  1. /* Read, sort and compare two directories.  Used for GNU DIFF.
  2.  
  3.    Copyright (C) 1988, 1989, 1992, 1993, 1994, 1995, 1998, 2001, 2002
  4.    Free Software Foundation, Inc.
  5.  
  6.    This file is part of GNU DIFF.
  7.  
  8.    GNU DIFF is free software; you can redistribute it and/or modify
  9.    it under the terms of the GNU General Public License as published by
  10.    the Free Software Foundation; either version 2, or (at your option)
  11.    any later version.
  12.  
  13.    GNU DIFF is distributed in the hope that it will be useful,
  14.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  15.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16.    GNU General Public License for more details.
  17.  
  18.    You should have received a copy of the GNU General Public License
  19.    along with this program; see the file COPYING.
  20.    If not, write to the Free Software Foundation,
  21.    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
  22.  
  23. #include "diff.h"
  24. #include <error.h>
  25. #include <exclude.h>
  26. #include <setjmp.h>
  27. #include <xalloc.h>
  28.  
  29. #ifdef __riscos
  30. # include <kernel.h>
  31. # include <swis.h>
  32. # include "alloca.h"
  33. #endif
  34.  
  35. /* Read the directory named by DIR and store into DIRDATA a sorted vector
  36.    of filenames for its contents.  DIR->desc == -1 means this directory is
  37.    known to be nonexistent, so set DIRDATA to an empty vector.
  38.    Return -1 (setting errno) if error, 0 otherwise.  */
  39.  
  40. struct dirdata
  41. {
  42.   size_t nnames;    /* Number of names.  */
  43.   char const **names;    /* Sorted names of files in dir, followed by 0.  */
  44.   char *data;    /* Allocated storage for file names.  */
  45. };
  46.  
  47. #ifndef __riscos
  48. /* Whether file names in directories should be compared with strcoll.  */
  49. static bool locale_specific_sorting;
  50.  
  51. /* Where to go if strcoll fails.  */
  52. static jmp_buf failed_strcoll;
  53. #endif
  54.  
  55. static bool dir_loop (struct comparison const *, int);
  56. static int compare_names_for_qsort (void const *, void const *);
  57.  
  58.  
  59. /* Read a directory and get its vector of names.  */
  60.  
  61. static bool
  62. dir_read (struct file_data const *dir, struct dirdata *dirdata)
  63. {
  64. #ifndef __riscos
  65.   register struct dirent *next;
  66. #endif
  67.   register size_t i;
  68.  
  69.   /* Address of block containing the files that are described.  */
  70.   char const **names;
  71.  
  72.   /* Number of files in directory.  */
  73.   size_t nnames;
  74.  
  75.   /* Allocated and used storage for file name data.  */
  76.   char *data;
  77.   size_t data_alloc, data_used;
  78.  
  79.   dirdata->names = 0;
  80.   dirdata->data = 0;
  81.   nnames = 0;
  82.   data = 0;
  83.  
  84. #ifdef __riscos
  85.  
  86. # if 0
  87.   if (dir->desc) {
  88. # else
  89.   {
  90. # endif
  91.     char *const buf=alloca(1024);
  92.     int r3=0, r4=0;
  93.     _kernel_oserror err;
  94.  
  95.     data_alloc = 256;
  96.     data_used = 0;
  97.     dirdata->data = data = xmalloc (data_alloc);
  98.     do {
  99.       const char *p=buf;
  100.       if (_swix (0xC/*OS_GBPB*/, 0x7F | 1<<28 | 1<<27,
  101.          9, dir->name, buf, 1024, r4, 1024, "*",
  102.          &r3, &r4)) break;
  103.       while (r3) {
  104.         int l = strlen (p) + 1;
  105.         if (!excluded_filename (excluded, p)) {
  106.           while (data_alloc < data_used + l)
  107.             dirdata->data = data = xrealloc (data, data_alloc *= 2);
  108.           memcpy (data + data_used, p, l);
  109.           data_used += l;
  110.           nnames++;
  111.         }
  112.         p+=l; r3--;
  113.       }
  114.     } while (r4>=0);
  115.   }
  116. #else
  117.   if (dir->desc != -1)
  118.     {
  119.       /* Open the directory and check for errors.  */
  120.       register DIR *reading = opendir (dir->name);
  121.       if (!reading)
  122.     return 0;
  123.  
  124.       /* Initialize the table of filenames.  */
  125.  
  126.       data_alloc = 512;
  127.       data_used = 0;
  128.       dirdata->data = data = xmalloc (data_alloc);
  129.  
  130.       /* Read the directory entries, and insert the subfiles
  131.      into the `data' table.  */
  132.  
  133.       while ((errno = 0, (next = readdir (reading)) != 0))
  134.     {
  135.       char *d_name = next->d_name;
  136.       size_t d_size = NAMLEN (next) + 1;
  137.  
  138.       /* Ignore "." and "..".  */
  139.       if (d_name[0] == '.'
  140.           && (d_name[1] == 0 || (d_name[1] == '.' && d_name[2] == 0)))
  141.         continue;
  142.  
  143.       if (excluded_filename (excluded, d_name))
  144.         continue;
  145.  
  146.       while (data_alloc < data_used + d_size)
  147.         {
  148.           if (PTRDIFF_MAX / 2 <= data_alloc)
  149.         xalloc_die ();
  150.           dirdata->data = data = xrealloc (data, data_alloc *= 2);
  151.         }
  152.  
  153.       memcpy (data + data_used, d_name, d_size);
  154.       data_used += d_size;
  155.       nnames++;
  156.     }
  157.       if (errno)
  158.     {
  159.       int e = errno;
  160.       closedir (reading);
  161.       errno = e;
  162.       return 0;
  163.     }
  164. #if CLOSEDIR_VOID
  165.       closedir (reading);
  166. #else
  167.       if (closedir (reading) != 0)
  168.     return 0;
  169. #endif
  170.     }
  171. #endif /* __riscos */
  172.  
  173.   /* Create the `names' table from the `data' table.  */
  174.   if (PTRDIFF_MAX / sizeof *names - 1 <= nnames)
  175.     xalloc_die ();
  176.   dirdata->names = names = xmalloc ((nnames + 1) * sizeof *names);
  177.   dirdata->nnames = nnames;
  178.   for (i = 0;  i < nnames;  i++)
  179.     {
  180.       names[i] = data;
  181.       data += strlen (data) + 1;
  182.     }
  183.   names[nnames] = 0;
  184.   return 1;
  185. }
  186.  
  187. /* Compare file names, returning a value compatible with strcmp.  */
  188.  
  189. static int
  190. compare_names (char const *name1, char const *name2)
  191. {
  192. #ifdef __riscos
  193.   return
  194.     ignore_file_name_case ? strcasecmp (name1, name2) : strcmp (name1, name2);
  195. #else
  196.   if (ignore_file_name_case)
  197.     {
  198.       int r = strcasecmp (name1, name2);
  199.       if (r)
  200.     return r;
  201.     }
  202.  
  203.   if (locale_specific_sorting)
  204.     {
  205.       int r;
  206.       errno = 0;
  207.       r = strcoll (name1, name2);
  208.       if (errno)
  209.     {
  210.       error (0, errno, _("cannot compare file names `%s' and `%s'"),
  211.          name1, name2);
  212.       longjmp (failed_strcoll, 1);
  213.     }
  214.       if (r)
  215.     return r;
  216.     }
  217.  
  218.   return file_name_cmp (name1, name2);
  219. #endif
  220. }
  221.  
  222. /* A wrapper for compare_names suitable as an argument for qsort.  */
  223.  
  224. static int
  225. compare_names_for_qsort (void const *file1, void const *file2)
  226. {
  227.   char const *const *f1 = file1;
  228.   char const *const *f2 = file2;
  229.   return compare_names (*f1, *f2);
  230. }
  231.  
  232. /* Compare the contents of two directories named in CMP.
  233.    This is a top-level routine; it does everything necessary for diff
  234.    on two directories.
  235.  
  236.    CMP->file[0].desc == -1 says directory CMP->file[0] doesn't exist,
  237.    but pretend it is empty.  Likewise for CMP->file[1].
  238.  
  239.    HANDLE_FILE is a caller-provided subroutine called to handle each file.
  240.    It gets three operands: CMP, name of file in dir 0, name of file in dir 1.
  241.    These names are relative to the original working directory.
  242.  
  243.    For a file that appears in only one of the dirs, one of the name-args
  244.    to HANDLE_FILE is zero.
  245.  
  246.    Returns the maximum of all the values returned by HANDLE_FILE,
  247.    or EXIT_TROUBLE if trouble is encountered in opening files.  */
  248.  
  249. int
  250. diff_dirs (struct comparison const *cmp,
  251.        int (*handle_file) (struct comparison const *,
  252.                    char const *, char const *))
  253. {
  254.   struct dirdata dirdata[2];
  255.   int volatile val = EXIT_SUCCESS;
  256.   int i;
  257.  
  258.   if ((cmp->file[0].desc == -1 || dir_loop (cmp, 0))
  259.       && (cmp->file[1].desc == -1 || dir_loop (cmp, 1)))
  260.     {
  261.       error (0, 0, "%s: recursive directory loop",
  262.          cmp->file[cmp->file[0].desc == -1].name);
  263.       return EXIT_TROUBLE;
  264.     }
  265.  
  266.   /* Get contents of both dirs.  */
  267.   for (i = 0; i < 2; i++)
  268.     if (! dir_read (&cmp->file[i], &dirdata[i]))
  269.       {
  270.     perror_with_name (cmp->file[i].name);
  271.     val = EXIT_TROUBLE;
  272.       }
  273.  
  274.   if (val == EXIT_SUCCESS)
  275.     {
  276.       char const **volatile names[2];
  277.       names[0] = dirdata[0].names;
  278.       names[1] = dirdata[1].names;
  279.  
  280. #ifndef __riscos
  281.       /* Use locale-specific sorting if possible, else native byte order.  */
  282.       locale_specific_sorting = 1;
  283.       if (setjmp (failed_strcoll))
  284.     locale_specific_sorting = 0;
  285. #endif
  286.  
  287.       /* Sort the directories.  */
  288.       for (i = 0; i < 2; i++)
  289.     qsort (names[i], dirdata[i].nnames, sizeof *dirdata[i].names,
  290.            compare_names_for_qsort);
  291.  
  292.       /* If `-S name' was given, and this is the topmost level of comparison,
  293.      ignore all file names less than the specified starting name.  */
  294.  
  295.       if (starting_file && ! cmp->parent)
  296.     {
  297.       while (*names[0] && compare_names (*names[0], starting_file) < 0)
  298.         names[0]++;
  299.       while (*names[1] && compare_names (*names[1], starting_file) < 0)
  300.         names[1]++;
  301.     }
  302.  
  303.       /* Loop while files remain in one or both dirs.  */
  304.       while (*names[0] || *names[1])
  305.     {
  306.       /* Compare next name in dir 0 with next name in dir 1.
  307.          At the end of a dir,
  308.          pretend the "next name" in that dir is very large.  */
  309.       int nameorder = (!*names[0] ? 1 : !*names[1] ? -1
  310.                : compare_names (*names[0], *names[1]));
  311.       int v1 = (*handle_file) (cmp,
  312.                    0 < nameorder ? 0 : *names[0]++,
  313.                    nameorder < 0 ? 0 : *names[1]++);
  314.       if (val < v1)
  315.         val = v1;
  316.     }
  317.     }
  318.  
  319.   for (i = 0; i < 2; i++)
  320.     {
  321.       if (dirdata[i].names)
  322.     free (dirdata[i].names);
  323.       if (dirdata[i].data)
  324.     free (dirdata[i].data);
  325.     }
  326.  
  327.   return val;
  328. }
  329.  
  330. /* Return nonzero if CMP is looping recursively in argument I.  */
  331.  
  332. static bool
  333. dir_loop (struct comparison const *cmp, int i)
  334. {
  335. #ifdef __riscos
  336.   return 0;
  337. #else
  338.   struct comparison const *p = cmp;
  339.   while ((p = p->parent))
  340.     if (0 < same_file (&p->file[i].stat, &cmp->file[i].stat))
  341.       return 1;
  342.   return 0;
  343. #endif
  344. }
  345.